package me.zhengjie.minbearwcs.serivce.mission.storageProcessor.locker.Floor5Or6.impl;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.minbearwcs.dao.agv.inf.AgvMissionDao;
import me.zhengjie.minbearwcs.dao.inbound.inf.InboundOrderDetailDao;
import me.zhengjie.minbearwcs.dao.stock.inf.PalletDao;
import me.zhengjie.minbearwcs.dao.storage.inf.LargeStorageDao;
import me.zhengjie.minbearwcs.dao.storage.inf.MediumStorageDao;
import me.zhengjie.minbearwcs.dao.storage.inf.SmallStorageDao;
import me.zhengjie.minbearwcs.dao.storage.inf.StorageDao;
import me.zhengjie.minbearwcs.entity.DTO.transfer.Tuple;
import me.zhengjie.minbearwcs.entity.PO.wcs.inbound.InboundOrderDetail;
import me.zhengjie.minbearwcs.entity.PO.wcs.stock.Pallet;
import me.zhengjie.minbearwcs.entity.PO.wcs.storage.MediumStorage;
import me.zhengjie.minbearwcs.entity.PO.wcs.storage.SmallStorage;
import me.zhengjie.minbearwcs.entity.PO.wcs.storage.type.StorageType;
import me.zhengjie.minbearwcs.serivce.mission.storageProcessor.locker.Floor5Or6.InboundStorage5Or6FloorLocker;
import org.springframework.stereotype.Component;

import java.util.*;


@Slf4j
@Component
@RequiredArgsConstructor
public class InboundStorage5Or6FloorLockerImpl implements InboundStorage5Or6FloorLocker {

    private final StorageDao storageDao;
    private final SmallStorageDao smallStorageDao;
    private final LargeStorageDao largeStorageDao;
    private final PalletDao palletDao;
    private final MediumStorageDao mediumStorageDao;
    private final AgvMissionDao agvmissidao;
    private final InboundOrderDetailDao inboundOrderDetailDao;

    /**
     * 5/6F储位查找策略：
     * 1.先找物料号，单号，批次号相同且已经放了货物还有空位的中储位
     * 2.找到同一订单的大储位
     * 3.找到有同一物料编号的楼层，按照大储位编号顺序，找到空的中储位，没有则找另外一层
     * <p>
     * 4。同一订单的大储位 -》放两个批次策略
     * 5.找到有同一物料编号的楼层，按照大储位编号顺序 -》放两个批次策略
     * 没有则找另外一层 -》放两个批次策略
     * <p>
     * 放两个批次策略：
     * 找到只放了一个批次 ，没有出入库锁定，且有空位的中储位
     */
    @Override
    public synchronized Tuple<String> lockStorage(String materialCode, String inboundOrder, String batchCode, String palletCode) {
        // 1.先找物料号，单号，批次号相同且已经放了货物还有空位的中储位
        List<String> mediumStorageList = storageDao.selectCanUseInboundMediumStorage(
                StorageType.STORAGE_AREA_5F,
                StorageType.STORAGE_AREA_6F,
                materialCode,
                inboundOrder,
                batchCode
        );

        for (String mediumStorage : mediumStorageList) {
            SmallStorage smallStorage = findSmallStorageInMediumStorage(mediumStorage);
            if (smallStorage == null) continue;
            smallStorageDao.updateInboundFreezePallet(smallStorage.getId(), palletCode);
            return Tuple.tuple(smallStorage.getQrPoint(), true);
        }

        // 2.找到同一订单的大储位
        List<String> largeStorageList = new ArrayList<>();
        largeStorageList = storageDao.selectSameInboundOrderLargeStorage(StorageType.STORAGE_AREA_5F, inboundOrder);
        if (largeStorageList.isEmpty()) {
            largeStorageList = storageDao.selectSameInboundOrderLargeStorage(StorageType.STORAGE_AREA_6F, inboundOrder);
        }
        if (!largeStorageList.isEmpty()) {
            for (String largeStorage : largeStorageList) {
                String mediumStorage = storageDao.selectEmptyMediumStorageByLargeStorage(largeStorage);
                SmallStorage smallStorage = findSmallStorageInEmptyMediumStorage(mediumStorage);
                if (smallStorage == null) continue;
                smallStorageDao.updateInboundFreezePallet(smallStorage.getId(), palletCode);
                mediumStorageDao.updateBatch1Info(mediumStorage, inboundOrder, materialCode, batchCode);
                return Tuple.tuple(smallStorage.getQrPoint(), true);
            }
        }

        // 3.找到有同一物料编号的楼层，按照大储位编号顺序，找到空的中储位，没有则找另外一层
        if (storageDao.selectHasMaterail(StorageType.STORAGE_AREA_5F, materialCode) > 0) {
            String mediumStorage = storageDao.selectEmptyMediumStorageByStorageType(StorageType.STORAGE_AREA_5F);
            if (mediumStorage != null) {
                SmallStorage smallStorage = findSmallStorageInEmptyMediumStorage(mediumStorage);
                if (smallStorage != null) {
                    smallStorageDao.updateInboundFreezePallet(smallStorage.getId(), palletCode);
                    mediumStorageDao.updateBatch1Info(mediumStorage, inboundOrder, materialCode, batchCode);
                    return Tuple.tuple(smallStorage.getQrPoint(), true);
                }
            }
        }

        if (storageDao.selectHasMaterail(StorageType.STORAGE_AREA_6F, materialCode) > 0) {
            String mediumStorage = storageDao.selectEmptyMediumStorageByStorageType(StorageType.STORAGE_AREA_6F);
            if (mediumStorage != null) {
                SmallStorage smallStorage = findSmallStorageInEmptyMediumStorage(mediumStorage);
                if (smallStorage != null) {
                    smallStorageDao.updateInboundFreezePallet(smallStorage.getId(), palletCode);
                    mediumStorageDao.updateBatch1Info(mediumStorage, inboundOrder, materialCode, batchCode);
                    return Tuple.tuple(smallStorage.getQrPoint(), true);
                }
            }
        }

        // 4.找一个空的中储位
        String randomMediumStorage = storageDao.selectEmptyMediumStorageByStorageType(StorageType.STORAGE_AREA_5F);
        if (randomMediumStorage != null) {
            SmallStorage smallStorage = findSmallStorageInEmptyMediumStorage(randomMediumStorage);
            if (smallStorage != null) {
                smallStorageDao.updateInboundFreezePallet(smallStorage.getId(), palletCode);
                mediumStorageDao.updateBatch1Info(randomMediumStorage, inboundOrder, materialCode, batchCode);
                return Tuple.tuple(smallStorage.getQrPoint(), true);
            }
        }

        randomMediumStorage = storageDao.selectEmptyMediumStorageByStorageType(StorageType.STORAGE_AREA_6F);
        if (randomMediumStorage != null) {
            SmallStorage smallStorage = findSmallStorageInEmptyMediumStorage(randomMediumStorage);
            if (smallStorage != null) {
                smallStorageDao.updateInboundFreezePallet(smallStorage.getId(), palletCode);
                mediumStorageDao.updateBatch1Info(randomMediumStorage, inboundOrder, materialCode, batchCode);
                return Tuple.tuple(smallStorage.getQrPoint(), true);
            }
        }


        // 5. 已经放了两个批次，有一个批次相同
        List<String> doubleBatchByBatch = storageDao.selectDoubleBatchByBatch(
                StorageType.STORAGE_AREA_5F,
                StorageType.STORAGE_AREA_6F,
                materialCode,
                inboundOrder,
                batchCode
        );
        for (String mediumStorage : doubleBatchByBatch) {
            SmallStorage smallStorage = findHasDoubleBatchMediumStorage(mediumStorage, materialCode, inboundOrder, batchCode);
            if (smallStorage == null) continue;
            smallStorageDao.updateInboundFreezePallet(smallStorage.getId(), palletCode);
            return Tuple.tuple(smallStorage.getQrPoint(), true);
        }


        // 5。已经放了一个批次
        List<String> OneBatchInboundMediumStorage = storageDao.selectHasOneBatchInboundMediumStorage(
                StorageType.STORAGE_AREA_5F,
                StorageType.STORAGE_AREA_6F
        );

        for (String mediumStorage : OneBatchInboundMediumStorage) {
            SmallStorage smallStorage = findHasOneBatchMediumStorage(mediumStorage, materialCode, inboundOrder, batchCode);
            if (smallStorage == null) continue;
            smallStorageDao.updateInboundFreezePallet(smallStorage.getId(), palletCode);
            MediumStorage mediumTemp = mediumStorageDao.selectByMediumStorage(mediumStorage);
            if (mediumTemp.getBatchOne() == null) {
                mediumStorageDao.updateBatch1Info(
                        mediumStorage, inboundOrder, materialCode, batchCode
                );
            } else {
                mediumStorageDao.updateBatch2Info(
                        mediumStorage, inboundOrder, materialCode, batchCode
                );
            }
            return Tuple.tuple(smallStorage.getQrPoint(), true);
        }

        return Tuple.tuple(null, false);
    }

    @Override
    public synchronized Tuple<String> lockAmend(String smallStorageCode) {
        SmallStorage smallStorage = smallStorageDao.selectBySmallStorage(smallStorageCode);
        MediumStorage mediumStorage = mediumStorageDao.selectByMediumStorage(smallStorage.getMediumStorageCode());
        List<SmallStorage> smallStorageList = smallStorageDao.selectSmallStorageByMediunStorage(smallStorage.getMediumStorageCode());

        Map<String, Boolean> palletHasAgvMissionMap = new HashMap<>();
        smallStorageList.forEach(a -> {
            boolean judgePalletHasAgvMission = agvmissidao.judgePalletHasAgvMission(a.getInboundFreezePallet());
            palletHasAgvMissionMap.put(a.getInboundFreezePallet(), judgePalletHasAgvMission);
        });


        // 1.多少个批次，多少入口
        int batchCount = 0;
        batchCount = batchCount + mediumStorage.getBatchOne() == null ? 0 : 1;
        batchCount = batchCount + mediumStorage.getBatchTwo() == null ? 0 : 1;
        if (batchCount == 1) {
            long enterCount = smallStorageList.stream()
                    .filter(a -> a.getIsEntrance() == 1)
                    .count();

            if (enterCount == 2) {
                Optional<SmallStorage> min = smallStorageList.stream()
                        .filter(a -> (a.getPalletCode() == null && a.getInboundFreezePallet() == null) ||
                                (a.getInboundFreezePallet() != null && !palletHasAgvMissionMap.getOrDefault(a.getInboundFreezePallet(), false))
                        )
                        .min(Comparator.comparingInt(SmallStorage::getColumnNum));

                if (!min.isPresent()) return Tuple.tuple(smallStorageCode, true);
                SmallStorage exchangeStorage = min.get();
                if (exchangeStorage.getSmallStorageCode().equals(smallStorageCode)) {
                    return Tuple.tuple(smallStorageCode, true);
                } else {
                    inboundStorageExchange(smallStorage, exchangeStorage);
                    return Tuple.tuple(exchangeStorage.getSmallStorageCode(), true);
                }

            } else if (enterCount == 1) {
                SmallStorage enter = smallStorageList.get(0);
                if (enter.getIsEntrance() == 1) {
                    for (int i = smallStorageList.size() - 1; i >= 0; i--) {
                        SmallStorage temp = smallStorageList.get(i);
                        if ((temp.getPalletCode() == null && temp.getInboundFreezePallet() == null) ||
                                (temp.getInboundFreezePallet() != null && !palletHasAgvMissionMap.getOrDefault(temp.getInboundFreezePallet(), false))) {
                            if (temp.getSmallStorageCode().equals(smallStorageCode)) {
                                return Tuple.tuple(smallStorageCode, true);
                            } else {
                                inboundStorageExchange(smallStorage, temp);
                                return Tuple.tuple(temp.getSmallStorageCode(), true);
                            }
                        }
                    }
                } else {
                    for (int i = 0; i < smallStorageList.size(); i++) {
                        SmallStorage temp = smallStorageList.get(i);
                        if ((temp.getPalletCode() == null && temp.getInboundFreezePallet() == null) ||
                                (temp.getInboundFreezePallet() != null && !palletHasAgvMissionMap.getOrDefault(temp.getInboundFreezePallet(), false))) {
                            if (temp.getSmallStorageCode().equals(smallStorageCode)) {
                                return Tuple.tuple(smallStorageCode, true);
                            } else {
                                inboundStorageExchange(smallStorage, temp);
                                return Tuple.tuple(temp.getSmallStorageCode(), true);
                            }
                        }
                    }
                }
            }

        } else if (batchCount == 2) {
            Map<String, Pallet> palletMap = new HashMap<>();

            for (SmallStorage tempSmallStorage : smallStorageList) {
                String palletCode = tempSmallStorage.getPalletCode();
                if (palletCode != null) {
                    Pallet pallet = palletDao.selectByPalletCode(palletCode);
                    palletMap.put(tempSmallStorage.getSmallStorageCode(), pallet);
                    continue;
                }
                String inboundFreezePallet = tempSmallStorage.getInboundFreezePallet();
                if (inboundFreezePallet != null) {
                    Pallet pallet = palletDao.selectByPalletCode(inboundFreezePallet);
                    palletMap.put(tempSmallStorage.getSmallStorageCode(), pallet);
                    continue;
                }
            }

            Optional<SmallStorage> min = smallStorageList.stream()
                    .filter(a -> a.getPalletCode() != null || a.getInboundFreezePallet() != null)
                    .min(Comparator.comparingInt(SmallStorage::getColumnNum));

            Optional<SmallStorage> max = smallStorageList.stream()
                    .filter(a -> a.getPalletCode() != null || a.getInboundFreezePallet() != null)
                    .max(Comparator.comparingInt(SmallStorage::getColumnNum));

            Pallet pallet = palletMap.get(smallStorageCode);

            if (min.isPresent() && max.isPresent()) {
                SmallStorage minStorage = min.get();
                Pallet minPallet = palletMap.get(minStorage.getSmallStorageCode());
                if (minPallet.getInboundOrderCode().equals(pallet.getInboundOrderCode()) &&
                        minPallet.getMaterialCode().equals(pallet.getMaterialCode()) &&
                        minPallet.getBatchCode().equals(pallet.getBatchCode())) {
                    Integer maxCol = smallStorageList.stream()
                            .filter(a -> {
                                Pallet temp = palletMap.get(a.getSmallStorageCode());
                                if (temp == null) return false;
                                if (!temp.getInboundOrderCode().equals(pallet.getInboundOrderCode()) ||
                                        !temp.getMaterialCode().equals(pallet.getMaterialCode()) ||
                                        !temp.getBatchCode().equals(pallet.getBatchCode())) {
                                    return true;
                                } else {
                                    return false;
                                }
                            })
                            .min(Comparator.comparingInt(SmallStorage::getColumnNum))
                            .get()
                            .getColumnNum();

                    Optional<SmallStorage> exchangeStorageOpt = smallStorageList.stream()
                            .filter(a -> a.getColumnNum() < maxCol)
                            .filter(a -> (a.getPalletCode() == null && a.getInboundFreezePallet() == null) ||
                                    (a.getInboundFreezePallet() != null && !palletHasAgvMissionMap.getOrDefault(a.getInboundFreezePallet(), false))
                            )
                            .min(Comparator.comparingInt(SmallStorage::getColumnNum));


                    if (!exchangeStorageOpt.isPresent()) return Tuple.tuple(smallStorageCode, true);
                    SmallStorage exchangeStorage = exchangeStorageOpt.get();
                    if (exchangeStorage.getSmallStorageCode().equals(smallStorageCode)) {
                        return Tuple.tuple(smallStorageCode, true);
                    } else {
                        inboundStorageExchange(smallStorage, exchangeStorage);
                        return Tuple.tuple(exchangeStorage.getSmallStorageCode(), true);
                    }

                }


                SmallStorage maxStorage = max.get();
                Pallet maxPallet = palletMap.get(maxStorage.getSmallStorageCode());
                if (maxPallet.getInboundOrderCode().equals(pallet.getInboundOrderCode()) &&
                        maxPallet.getMaterialCode().equals(pallet.getMaterialCode()) &&
                        maxPallet.getBatchCode().equals(pallet.getBatchCode())) {
                    Integer minCol = smallStorageList.stream()
                            .filter(a -> {
                                Pallet temp = palletMap.get(a.getSmallStorageCode());
                                if (temp == null) return false;
                                if (!temp.getInboundOrderCode().equals(pallet.getInboundOrderCode()) ||
                                        !temp.getMaterialCode().equals(pallet.getMaterialCode()) ||
                                        !temp.getBatchCode().equals(pallet.getBatchCode())) {
                                    return true;
                                } else {
                                    return false;
                                }
                            })
                            .max(Comparator.comparingInt(SmallStorage::getColumnNum))
                            .get()
                            .getColumnNum();

                    Optional<SmallStorage> exchangeStorageOpt = smallStorageList.stream()
                            .filter(a -> a.getColumnNum() > minCol)
                            .filter(a -> (a.getPalletCode() == null && a.getInboundFreezePallet() == null) ||
                                    (a.getInboundFreezePallet() != null && !palletHasAgvMissionMap.getOrDefault(a.getInboundFreezePallet(), false))
                            )
                            .min(Comparator.comparingInt(SmallStorage::getColumnNum));


                    if (!exchangeStorageOpt.isPresent()) return Tuple.tuple(smallStorageCode, true);
                    SmallStorage exchangeStorage = exchangeStorageOpt.get();
                    if (exchangeStorage.getSmallStorageCode().equals(smallStorageCode)) {
                        return Tuple.tuple(smallStorageCode, true);
                    } else {
                        inboundStorageExchange(smallStorage, exchangeStorage);
                        return Tuple.tuple(exchangeStorage.getSmallStorageCode(), true);
                    }
                }

            }
        }


        return Tuple.tuple(smallStorageCode, true);
    }


    private void inboundStorageExchange(SmallStorage smallStorage1, SmallStorage smallStorage2) {
        InboundOrderDetail inboundOrderDetail1 = inboundOrderDetailDao.selectByPalletAndStoragePoint(smallStorage1.getInboundFreezePallet(), smallStorage1.getQrPoint());
        InboundOrderDetail inboundOrderDetail2 = inboundOrderDetailDao.selectByPalletAndStoragePoint(smallStorage2.getInboundFreezePallet(), smallStorage2.getQrPoint());

        if(inboundOrderDetail1 != null && inboundOrderDetail2 != null){
            inboundOrderDetailDao.updateStoragePointById(inboundOrderDetail1.getId(), smallStorage2.getQrPoint());
            inboundOrderDetailDao.updateStoragePointById(inboundOrderDetail2.getId(), smallStorage1.getQrPoint());

        }else if(inboundOrderDetail1 == null && inboundOrderDetail2 != null){
            inboundOrderDetailDao.updateStoragePointById(inboundOrderDetail2.getId(), smallStorage1.getQrPoint());
        }else if(inboundOrderDetail1 != null && inboundOrderDetail2 == null){
            inboundOrderDetailDao.updateStoragePointById(inboundOrderDetail1.getId(), smallStorage2.getQrPoint());
        }

        smallStorageDao.updateInboundFreezePallet(smallStorage1.getId(), smallStorage2.getInboundFreezePallet());
        smallStorageDao.updateInboundFreezePallet(smallStorage2.getId(), smallStorage1.getInboundFreezePallet());

    }


    private SmallStorage findSmallStorageInMediumStorage(String mediumStorage) {
        List<SmallStorage> smallStorageList = smallStorageDao.selectSmallStorageByMediunStorage(mediumStorage);

        long outboundCount = smallStorageList.stream()
                .filter(a -> a.getOutboundFreezePallet() != null)
                .count();

        if (outboundCount > 0) return null;

        long enterCount = smallStorageList.stream()
                .filter(a -> a.getIsEntrance() == 1)
                .count();

        // 两口选最小
        if (enterCount == 2) {
            Optional<SmallStorage> min = smallStorageList.stream()
                    .filter(a -> a.getPalletCode() == null && a.getInboundFreezePallet() == null)
                    .min(Comparator.comparingInt(SmallStorage::getColumnNum));

            if (min.isPresent()) {
                return min.get();
            }
        }

        // 一口选对侧
        if (enterCount == 1) {
            SmallStorage smallStorage = smallStorageList.get(0);
            if (smallStorage.getIsEntrance() == 1) {
                for (int i = smallStorageList.size() - 1; i >= 0; i--) {
                    SmallStorage temp = smallStorageList.get(i);
                    if (temp.getPalletCode() == null && temp.getInboundFreezePallet() == null) {
                        return temp;
                    }
                }
            } else {
                for (int i = 0; i < smallStorageList.size(); i++) {
                    SmallStorage temp = smallStorageList.get(i);
                    if (temp.getPalletCode() == null && temp.getInboundFreezePallet() == null && temp.getOutboundFreezePallet() == null) {
                        return temp;
                    }
                }
            }
        }

        return null;
    }

    private SmallStorage findSmallStorageInEmptyMediumStorage(String mediumStorage) {
        List<SmallStorage> smallStorageList = smallStorageDao.selectSmallStorageByMediunStorage(mediumStorage);

        long outboundCount = smallStorageList.stream()
                .filter(a -> a.getOutboundFreezePallet() != null)
                .count();

        if (outboundCount > 0) return null;

        long enterCount = smallStorageList.stream()
                .filter(a -> a.getIsEntrance() == 1)
                .count();

        // 两口选最小
        if (enterCount == 2) {
            Optional<SmallStorage> optional = smallStorageList.stream()
                    .min(Comparator.comparingInt(SmallStorage::getColumnNum));

            if (optional.isPresent()) {
                return optional.get();
            }
        }

        // 一口选对侧
        if (enterCount == 1) {
            SmallStorage smallStorage = smallStorageList.get(0);
            if (smallStorage.getIsEntrance() == 1) {
                return smallStorageList.get(smallStorageList.size() - 1);
            } else {
                return smallStorage;
            }
        }

        return null;
    }

    private SmallStorage findHasDoubleBatchMediumStorage(String mediumStorage, String materialCode, String inboundOrder, String batchCode) {
        List<SmallStorage> smallStorageList = smallStorageDao.selectSmallStorageByMediunStorage(mediumStorage);

        long outboundCount = smallStorageList.stream()
                .filter(a -> a.getOutboundFreezePallet() != null)
                .count();

        if (outboundCount > 0) return null;

        long enterCount = smallStorageList.stream()
                .filter(a -> a.getIsEntrance() == 1)
                .count();

        if (enterCount < 2) return null;


        Map<String, Pallet> palletMap = new HashMap<>();

        for (SmallStorage smallStorage : smallStorageList) {
            String palletCode = smallStorage.getPalletCode();
            if (palletCode != null) {
                Pallet pallet = palletDao.selectByPalletCode(palletCode);
                palletMap.put(smallStorage.getSmallStorageCode(), pallet);
                continue;
            }
            String inboundFreezePallet = smallStorage.getInboundFreezePallet();
            if (inboundFreezePallet != null) {
                Pallet pallet = palletDao.selectByPalletCode(inboundFreezePallet);
                palletMap.put(smallStorage.getSmallStorageCode(), pallet);
                continue;
            }
        }


        Optional<SmallStorage> min = smallStorageList.stream()
                .filter(a -> a.getPalletCode() != null || a.getInboundFreezePallet() != null)
                .min(Comparator.comparingInt(SmallStorage::getColumnNum));

        Optional<SmallStorage> max = smallStorageList.stream()
                .filter(a -> a.getPalletCode() != null || a.getInboundFreezePallet() != null)
                .max(Comparator.comparingInt(SmallStorage::getColumnNum));


        if (min.isPresent() && max.isPresent()) {
            SmallStorage minStorage = min.get();
            Pallet minPallet = palletMap.get(minStorage.getSmallStorageCode());
            if (minPallet.getInboundOrderCode().equals(inboundOrder) &&
                    minPallet.getMaterialCode().equals(materialCode) &&
                    minPallet.getBatchCode().equals(batchCode)) {
                Optional<SmallStorage> optional = smallStorageList.stream()
                        .filter(a -> a.getColumnNum() == (min.get().getColumnNum() - 1))
                        .filter(a -> a.getPalletCode() != null || a.getInboundFreezePallet() != null)
                        .findFirst();
                return optional.orElse(null);
            }


            SmallStorage maxStorage = max.get();
            Pallet maxPallet = palletMap.get(maxStorage.getSmallStorageCode());
            if (maxPallet.getInboundOrderCode().equals(inboundOrder) &&
                    maxPallet.getMaterialCode().equals(materialCode) &&
                    maxPallet.getBatchCode().equals(batchCode)) {
                Optional<SmallStorage> optional = smallStorageList.stream()
                        .filter(a -> a.getColumnNum() == (max.get().getColumnNum() + 1))
                        .filter(a -> a.getPalletCode() != null || a.getInboundFreezePallet() != null)
                        .findFirst();
                return optional.orElse(null);
            }

        }


        return null;
    }


    private SmallStorage findHasOneBatchMediumStorage(String mediumStorage, String materialCode, String inboundOrder, String batchCode) {
        List<SmallStorage> smallStorageList = smallStorageDao.selectSmallStorageByMediunStorage(mediumStorage);

        long outboundCount = smallStorageList.stream()
                .filter(a -> a.getOutboundFreezePallet() != null)
                .count();

        if (outboundCount > 0) return null;

        long enterCount = smallStorageList.stream()
                .filter(a -> a.getIsEntrance() == 1)
                .count();

        if (enterCount < 2) return null;


        Optional<SmallStorage> min = smallStorageList.stream()
                .filter(a -> a.getPalletCode() != null || a.getInboundFreezePallet() != null)
                .min(Comparator.comparingInt(SmallStorage::getColumnNum));

        Optional<SmallStorage> max = smallStorageList.stream()
                .filter(a -> a.getPalletCode() != null || a.getInboundFreezePallet() != null)
                .max(Comparator.comparingInt(SmallStorage::getColumnNum));

        Optional<SmallStorage> minEnter = smallStorageList.stream()
                .filter(a -> a.getIsEntrance() == 1)
                .min(Comparator.comparingInt(SmallStorage::getColumnNum));

        Optional<SmallStorage> maxEnter = smallStorageList.stream()
                .filter(a -> a.getIsEntrance() == 1)
                .max(Comparator.comparingInt(SmallStorage::getColumnNum));


        if (min.isPresent() && max.isPresent() && minEnter.isPresent() && maxEnter.isPresent()) {
            int minCost = min.get().getColumnNum() - minEnter.get().getColumnNum();
            int maxCost = maxEnter.get().getColumnNum() - max.get().getColumnNum();

            if (minCost > maxCost) {
                Optional<SmallStorage> optional = smallStorageList.stream()
                        .filter(a -> a.getColumnNum() == (min.get().getColumnNum() - 1))
                        .filter(a -> a.getPalletCode() != null || a.getInboundFreezePallet() != null)
                        .findFirst();
                if (optional.isPresent()) {
                    return optional.get();
                }
            }

            if (minCost <= maxCost) {
                Optional<SmallStorage> optional = smallStorageList.stream()
                        .filter(a -> a.getColumnNum() == (max.get().getColumnNum() + 1))
                        .filter(a -> a.getPalletCode() != null || a.getInboundFreezePallet() != null)
                        .findFirst();
                if (optional.isPresent()) {
                    return optional.get();
                }
            }
        }


        return null;
    }

    @Override
    public synchronized Tuple<String> lockExchangeStorage(String palletCode, String storageQrCode) {
        StorageType storageType = storageDao.selectLargeStorageTypeByQrPoint(storageQrCode);

        StorageType exchangeType = null;

        switch (storageType) {
            case STORAGE_AREA_5F:
                exchangeType = StorageType.EXCHANGE_AREA_5F;
                break;
            case STORAGE_AREA_6F:
                exchangeType = StorageType.EXCHANGE_AREA_6F;
                break;
        }

        SmallStorage smallStorage = storageDao.searchRandomEnterStorage(exchangeType);
        if (smallStorage == null) {
            return Tuple.tuple(null, false);
        }

        smallStorageDao.updateInboundFreezePallet(smallStorage.getId(), palletCode);
        return Tuple.tuple(smallStorage.getQrPoint(), true);
    }

    @Override
    public boolean arriveAndUnfreezeStorage(String smallStoargeCode, String palletCode) {
        return smallStorageDao.updatePalletCodeAndClearInboundFreeze(
                smallStoargeCode,
                palletCode
        );
    }

    @Override
    public synchronized boolean unLockStorage(String smallStoargeCode) {
        SmallStorage smallStorage = smallStorageDao.selectBySmallStorage(smallStoargeCode);
        if (!(smallStorage.getPalletCode() != null || smallStorage.getInboundFreezePallet() != null)) {
            return true;
        }

        List<SmallStorage> smallStorageList = smallStorageDao.selectSmallStorageByMediunStorage(smallStorage.getMediumStorageCode());

        Map<String, Pallet> palletMap = new HashMap<>();

        for (SmallStorage tempSmall : smallStorageList) {
            String tempPalletCode = tempSmall.getPalletCode();
            if (tempPalletCode != null) {
                Pallet pallet = palletDao.selectByPalletCode(tempPalletCode);
                palletMap.put(tempSmall.getSmallStorageCode(), pallet);
                continue;
            }
            String inboundFreezePallet = tempSmall.getInboundFreezePallet();
            if (inboundFreezePallet != null) {
                Pallet pallet = palletDao.selectByPalletCode(inboundFreezePallet);
                palletMap.put(tempSmall.getSmallStorageCode(), pallet);
                continue;
            }
        }


        long count = smallStorageList.stream()
                .filter(a -> !a.getSmallStorageCode().equals(smallStoargeCode))
                .filter(b -> {
                    Pallet pallet = palletMap.get(smallStoargeCode);
                    Pallet palletTmep = palletMap.get(b.getSmallStorageCode());
                    if (pallet == null || palletTmep == null) return false;

                    return pallet.getInboundOrderCode().equals(palletTmep.getInboundOrderCode()) &&
                            pallet.getMaterialCode().equals(palletTmep.getMaterialCode()) &&
                            pallet.getBatchCode().equals(palletTmep.getMaterialCode());
                })
                .count();

        if (count == 0) {
            MediumStorage mediumStorage = mediumStorageDao.selectByMediumStorage(smallStorage.getMediumStorageCode());
            Pallet pallet = palletMap.get(smallStoargeCode);
            if (pallet.getInboundOrderCode().equals(mediumStorage.getInboundOrderOne()) &&
                    pallet.getMaterialCode().equals(mediumStorage.getMaterialOne()) &&
                    pallet.getBatchCode().equals(mediumStorage.getBatchOne())) {
                mediumStorageDao.updateBatch1Info(
                        smallStorage.getMediumStorageCode(),
                        null, null, null
                );
            }
            if (pallet.getInboundOrderCode().equals(mediumStorage.getInboundOrderTwo()) &&
                    pallet.getMaterialCode().equals(mediumStorage.getMaterialTwo()) &&
                    pallet.getBatchCode().equals(mediumStorage.getBatchTwo())) {
                mediumStorageDao.updateBatch2Info(
                        smallStorage.getMediumStorageCode(),
                        null, null, null
                );
            }
        }

        smallStorageDao.clearStoragePallet(smallStoargeCode);


        return true;
    }


}
